<?php


/* vim: set expandtab tabstop=4 shiftwidth=4: *
// +----------------------------------------------------------------------+
// | PHP version 4                                                        |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2002 The PHP Group                                |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.0 of the PHP license,       |
// | that is bundled with this package in the file LICENSE, and is        |
// | available at through the world-wide-web at                           |
// | http://www.php.net/license/2_02.txt.                                 |
// | If you did not receive a copy of the PHP license and are unable to   |
// | obtain it through the world-wide-web, please send a note to          |
// | license@php.net so we can mail you a copy immediately.               |
// +----------------------------------------------------------------------+
// | Authors: Sebastiao Rocha A Neto <rocha@i-node.com.br>                |
// +----------------------------------------------------------------------+
//
// $Id: Report.php,v 1.2 2011-09-09 02:18:52 herve Exp $
/**
 * PHP Report:
 * 
 * Generates a html report page from a result set
 * array. That usually came from a sql query
 * and isnt well formatted for display. This class
 * 'prettyfy' the result using a template class, either
 * IT,ITX or PHPLIB template
 * 
 * Ex.: 
 * 
 * Inicial result set:
 * ----------------------------------------------
 * title    subtitle    value1    value2    value2
 * ----------------------------------------------
 * titulo1    subtitulo1    valor1    valor2    valor3
 * titulo1    subtitulo1    valor1    valor2    valor3
 * titulo1    subtitulo1    total1    total2    total3
 * titulo1    subtitulo2    valor1    valor2    valor3
 * titulo1    subtitulo2    valor1    valor2    valor3
 * titulo1    subtitulo2    total1    total2    total3
 * titulo2    subtitulo1    valor1    valor2    valor3
 * titulo2    subtitulo1    valor1    valor2    valor3
 * titulo2    subtitulo1    total1    total2    total3
 * titulo2    subtitulo2    valor1    valor2    valor3
 * titulo2    subtitulo2    total1    total2    total3
 *
 * 
 * Report output
 * ============================================
 * titulo1
 * ---------------------------------------------
 *       subtitulo1
 *       ----------------------------
 *           valor1 valor2 valor3
 *           valor1 valor2 valor3
 *       subtitulo2
 *       ----------------------------
 *           valor1 valor2 valor3
 *           valor1 valor2 valor3
 * ============================================
 * titulo2
 * ---------------------------------------------
 *       subtitulo1
 *       ----------------------------
 *             valor1 valor2 valor3
 *             valor1 valor2 valor3
 *       subtitulo2
 *       ----------------------------
 *           valor1 valor2 valor3
 *           valor1 valor2 valor3
 * ---------------------------------------------- 
 *
 * The format is done on the template file like:
 *
 * <!-- BEGIN title -->
 * {title}
 * ---------------------------------------------
 * <!-- BEGIN subtitle -->
 * {subtitle}
 * ----------------------------
 *     <!-- BEGIN values -->
 *     {value1} {value2} {value3}
 *     <!-- END values -->
 *   <!-- BEGIN totals -->
 *     {total1} {total2} {total3}
 *     <!-- END totals -->
 *
 * <!-- END subtitle -->
 * ----------------------------------------------
 * <!-- END title -->
 * 
 **/
require_once ("Report_template.php");
//require_once("Report_IT.php");
class Report extends ReportTemplate {
	/**
	 * Array with columns names used for parse values '0' as null
	 * @access private
	 * @var array
	 **/
	var $_nullforzero = array ();

	/**
	 * Raw result array
	 * @access private
	 * @var array 
	 **/
	var $_resultset = array ();

	/**
	 * Array with columns names used for (sub)titles
	 * @access private 
	 * @var array 
	 **/
	var $_title = array ();

	/**
	 * Number of subtitle levels
	 *
	 * @access private
	 * @var integer 
	 **/
	var $_lastlevel = -1;

	/**
	 * New array with the new rewritten array
	 * 
	 * @access private 
	 * @var array
	 **/
	var $_result = array ();

	/**
	 * Boolean showtotals, if true the last line of 
	 * block values is total, and have a template block
	 * apart to be parsed.
	 * 
	 * @access private
	 * @var boolean 
	 **/
	var $_showtotals = false;

	/**
	 *
	 * @access private
	 * @var string
	 **/
	var $_blockvaluename = 'values';

	/**
	 * @access private
	 * @var string
	 **/
	var $_blocktotalname = 'totals';

	/**
	 * Thousand separator (pt_BR stile)
	 * 
	 * @access private
	 * @var string
	 **/
	var $_thousand_sep = ',';
	/**
	 * Decimal point (pt_BR style)
	 *
	 * @access private
	 * @var string
	 **/
	var $_dec_point = '.';

	/**
	 * Currency symbol (Brazilian real)
	 *
	 * @access private
	 * @var string
	 **/
	var $_cur_symbol = 'R$';

	/**
	 * HTML style to show negative values
	 * 
	 * @access private
	 * @var string
	 **/
	var $_negativestyle = '<font color="#FF0000">(%s)</font>';

	/**
	 * Variable type for parsing befor show.
	 * Types: numeric, money, date
	 * 
	 * @access private
	 * @var array
	 **/
	var $_variabletype = array ();

	/**
	 * Number of decimals
	 *
	 * @access private
	 * @var integer
	 **/
	var $_decimals = 2;

	/**
	 *
	 * @access private
	 * @var array
	 **/
	var $_totals = array ();

	/**
	 * Date regex, use to extract fields from database columns
	 * @access private
	 * @var string
	 **/
	var $_dateregex = "([0-9]{1,4})-([0-9]{1,2})-([0-9]{1,2})";

	/**
	 *
	 * @access private
	 * @var string
	 **/
	var $_datetimeregex = "([0-9]{1,4})-([0-9]{1,2})-([0-9]{1,2})\s+([^\s]+)";

	/**
	 * Use european style for days (DD/MM/YYYY) or (MM/DD/YYY)?
	 * @access public
	 * @var string
	 **/
	var $europeanstyle = true;

	var $_expression = array ();

	var $_functions = array ();

	var $doTotals = array ();

	/**
	 * Report::Report()
	 *
	 * Constructor
	 * @access protected 
	 * @param  $result
	 **/
	function Report(& $resultset, $dir, $file) {
		$this->setResultSet($resultset);
		$this->debug = false;
		$this->_file = $file;
		$this->_dir = $dir;
		$this->setup();
	}

	/**
	 * Report::setTitle()
	 *
	 * Template independent function.
	 * Set the name of columns to use as title/subtitles. $level tells the
	 * position the title in the hierachie. Accepts an array or a pair (title, level)
	 *
	 * @param  $title
	 * @param unknown $level
	 * @return void
	 **/
	function setTitle($title, $level = null) {
		if (isset ($level)) {
			$this->_title[$level] = $title;
			$this->_lastlevel = $level;
		} else {
			if (is_array($title)) {
				foreach ($title as $level => $name) {
					$this->_title[$level] = $name;
					$this->_lastlevel = $level;
				}
			} else {
				/**
				 * Raise error
				 **/
				return new PEAR_Error("Unable to set title, unknown args.", -1);
			}
		}
	}

	/**
	 * Report::rewriteArray()
	 *
	 * Template independent function.
	 * Remove one column from the array and returns a new array
	 *
	 * Needs a array:
	 *                     title    value1  value2
	 *                     ------------------------
	 * array ( '1' => array(titulo1,  valor1,  valor2),
	 *         '2' => array(titulo1,  valor1,  valor2),
	 *         '3' => array(titulo1,  valor1,  valor2)
	 * )
	 *
	 * Returns
	 * array (
	 *       'titulo1' => array( '1' => array( valor1,  valor2),
	 *                           '2' => array( valor1,  valor2),
	 *                           '3' => array( valor1,  valor2)
	 *                         )
	 *       )
	 * @param  $title
	 * @param  $oldarray
	 * @return
	 **/
	function & rewriteArray($colname, & $oldarray) {
		$part = array ();
		foreach ($oldarray as $idx => $list) {
			$name = $list[$colname];
			if (isset ($count[$name])) {
				$count[$name]++;
			} else {
				$count[$name] = 0;
			}
			unset ($list[$colname]);
			$part[$name][$count[$name]] = $list;
		}
		// print_r($part);
		return $part;
	}

	/**
	 * Report::rewriteResultSet()
	 *
	 * Template independent function.
	 * Recursive function to write the final array as
	 * $result[$title][$subtitle]...[] = array ( value1 => 'valor1', ...etc);
	 *
	 * @param string $list
	 * @return mixed array with parcial result written.
	 **/
	function & rewriteResultSet($list = "") {
		static $level = 0;

		$title = $this->_title[$level];
		if (!$level) {
			if (!$list) {
				$list = & $this->getResult(); // (i) => array()
			}
			$list = & $this->rewriteArray($title, $list); //  (name)(i) => array()
			$level++;
			foreach ($list as $titlevalue => $newlist) {
				$reslist[$titlevalue] = $this->rewriteResultSet($newlist);
			}
			$level--; // back one level
			return $reslist;
		}
		elseif ($level <= $this->_lastlevel) {
			$list = & $this->rewriteArray($title, $list);
			$level++;
			foreach ($list as $titlevalue => $newlist) {
				$reslist[$titlevalue] = $this->rewriteResultSet($newlist);
			}
			$level--; // back one level
			return $reslist;
		} else {
			return $list;
		}
	}

	/**
	 * Report::makeReport()
	 *
	 * Recursive function to create the report
	 * from the rewritten array
	 *
	 * @param string $list
	 * @return
	 **/
	function makeReport($list = "") {
		static $showlevel = 0;

		if (!$list && isset ($this->_title[$showlevel])) {
			$list = $this->rewriteResultSet();
		}
		elseif (!$list) {
			$list = $this->getResult(); // (i) => array()
		}
		if (!$showlevel && isset ($this->_title[$showlevel])) {
			$titlename = $this->_title[$showlevel];
			$showlevel++;
			foreach ($list as $title => $newlist) {
				$this->_titlevalues[$titlename] = $title;
				$this->makeReport($newlist);
				$this->blockParser($titlename, $title);
				$this->cleanBlock($this->_title[$showlevel]); // Only used in phplib template.inc
			}
			// Finishing the recursion, show the gran total if we were asked to.
			if ($this->_blockgrantotalname) {
				$this->parseFinal($this->_grantotals, $this->_blockgrantotalname);
			}
			$showlevel--;
		}
		elseif (isset ($this->_lastlevel) && ($showlevel <= $this->_lastlevel)) {
			$titlename = $this->_title[$showlevel];
			$showlevel++;
			foreach ($list as $title => $newlist) {
				$this->_titlevalues[$titlename] = $title;
				$this->makeReport($newlist);
				$this->blockParser($titlename, $title);
				$this->cleanBlock($this->_title[$showlevel]); // Only used in phplib template.inc
			}
			$showlevel--;
		} else {
			/**
			 * End of line, parse the values
			 * Set variables values
			 **/
			$this->parseVariables($list);
		}
	}
	/**
	*
	*/
	function setGranTotalBlock($val) {
		$this->_blockgrantotalname = $val;
	}
	/**
	 * @access public
	 * @return array
	 **/
	function setNullForZero($zeroValue) {
		if (is_array($zeroValue)) {
			foreach ($zeroValue as $zero) {
				$this->_nullforzero[$zero] = true;
			}
		}
		elseif (is_string($zeroValue)) {
			$this->_nullforzero[$zeroValue] = true;
		}
	}

	/**
	 * Get the current result array
	 *
	 * @access public
	 * @return void
	 **/
	function & getResult() {
		if (!$this->_result) {
			return $this->_resultset;
		} else {
			return $this->_result;
		}
	}

	/**
	 * @access public
	 * @return array
	 **/
	function setResult($newValue) {
		$this->_result = $newValue;
	}

	/**
	 * Get the raw resultset array
	 *
	 * @access public
	 * @return array
	 **/
	function getResultSet() {
		return $this->_resultset;
	}

	/**
	 * Set the raw result set array
	 * @access public
	 * @return array
	 **/
	function setResultSet($newValue) {
		/**
		 * Since we can have result columns coming
		 * from evaluation of expressions, if they exists we insert then
		 * in the resultset. The value here doesnt matter yet.
		 **/
		$temp = $newValue;
		if (is_array($this->_expression)) {
			foreach ($this->_expression as $name => $value) {
				//echo "Set result: $name = $value <br>";
				foreach ($temp as $i => $dummy) {
					$newValue[$i][$name] = '';
				}
			}
		}
		$this->_resultset = & $newValue;
	}

	/**
	 * Get the number of subtitles
	 * @access public
	 * @return integer
	 **/
	function getLastLevel() {
		return $this->_lastlevel;
	}

	/**
	 * Set the number of subtitles
	 * @access public
	 * @return void
	 **/
	function setLastLevel($newValue) {
		$this->_lastlevel = $newValue;
	}

	/**
	 * @access public
	 * @return boolean
	 **/
	function getShowTotals() {
		return $this->_showtotals;
	}

	/**
	 * @access public
	 * @return void
	 **/
	function setShowTotals($newValue) {
		$this->_showtotals = $newValue;
	}
	/**
	 *
	 * @access public
	 * @return string
	 **/
	function getBlockValueName() {
		return $this->_blockvaluename;
	}

	/**
	 *
	 * @access public
	 * @return void
	 **/
	function setBlockValueName($newValue) {
		$this->_blockvaluename = $newValue;
	}

	/**
	 *
	 * @access public
	 * @return string
	 **/
	function getBlockTotalName() {
		return $this->_blocktotalname;
	}

	/**
	 *
	 * @access public
	 * @return void
	 **/
	function setBlockTotalName($newValue) {
		$this->_blocktotalname = $newValue;
	}

	/**
	 * Report::setVar()
	 *
	 *  Wrapper for setVariable's IT method.
	 *  It does some formatting before set parse template
	 *  variables. Also accumlate totals if that was set.
	 *
	 * @param $variable
	 * @param string $value
	 * @return
	 **/
	function setVar($variable, $value = "") {
		if (is_array($variable)) {
			$this->_currentlist = $variable;
			foreach ($variable as $name => $value) {
				/**
				 * See if we are collecting totals
				 *
				 **/
				if ($this->_totalizing) {
					/**
					 * See if we have a expression to eval.
					 */
					if (isset ($this->_expression[$name])) {
						if ($this->_expression[$name]) {
							$value = $this->evaluateExpression($name);
						}
					}
					if (isset ($this->_expression[$name])) {
						if ($this->_functions[$name]) {
							$value = $this->evaluateFunction($name, $value);
						}
					}
					/**
					 * See if we have to do some accounting.
					 */
					if (isset ($this->doTotals[$name])) {
						if ($this->doTotals[$name]) {
							$this->_totals[$name] += $value;
							$this->_grantotals[$name] += $value;
						}
					}
				}
				/**
				 * Parse the value.
				 */
				$newvalue = $this->parseValue($name, $value);
				$this->replaceVar($name, $newvalue);
			}
		} else {
			/**
			 * See if we have to do some accounting.
			 */
			if ($this->doTotals[$variable]) {
				$this->_totals[$variable] += $value;
			}
			/**
			 * See if we have to do some accounting.
			 */
			if ($this->_functions[$variable]) {
				$value = $this->evaluateFunction($variable, $value);
			}
			/**
			 * See if we have a expression to eval.
			 */
			if ($this->_expression[$variable]) {
				$value = $this->evaluateExpression($variable);
			}
			/**
			* Parse the value.
			*/
			$newvalue = $this->parseValue($variable, $value);
			$this->replaceVar($variable, $newvalue);
		}
	}
	/**
	 *
	 * @access public
	 * @return string
	 **/
	function getTotals() {
		return $this->_totals;
	}

	/**
	 *
	 * @access public
	 * @return array
	 **/
	function setTotals($newValue) {
		if (isset ($newValue)) {
			$this->_showtotals = $newValue;
		}
		$this->doTotals = $newValue;
	}

	/**
	 * Report::evaluateFunction()
	 *
	 * @param $name
	 * @return
	 **/
	function evaluateFunction($name, $value) {
		$function = $this->getFunction($name);
		$val = $function ($value);
		return $val;
	}

	/**
	 * Report::getFunction()
	 *
	 * @param $name
	 * @return
	 **/
	function getFunction($name) {
		return $this->_functions[$name];
	}

	/**
	 * Report::setFunctions()
	 *
	 * @param $name
	 * @return
	 **/
	function setFunctions($f) {
		$this->_functions = $f;
	}

	/**
	 * Report::evaluateExpression()
	 *
	 * @param $name
	 * @return
	 **/
	function evaluateExpression($name) {
		$expression = $this->getExpression($name);
		foreach ($this->_currentlist as $var => $value) {
			if (!isset ($value)) {
				$value = "0";
			}
			$expression = str_replace("{" . $var . "}", $value, $expression);
		}
		// Some var can be at the title array, we dont want do miss them.
		if ($this->_titlevalues) {
			reset($this->_titlevalues);
			while (list ($var, $value) = each($this->_titlevalues)) {
				$expression = str_replace("{" . $var . "}", $value, $expression);
			}
			reset($this->_titlevalues);
		}

		eval ("\$expression = $expression;");
		// Since we added the new column for the expression without a value now we load it.
		$this->_currentlist[$name] = $expression;
		return $expression;
	}

	/**
	 * Report::getExpression()
	 *
	 * @param $name
	 * @return
	 **/
	function getExpression($name) {
		return $this->_expression[$name];
	}

	/**
	 * Report::setExpression()
	 *
	 * @param $name
	 * @param $expression
	 * @return
	 **/
	function setExpression($name, $expression, $type = "numeric") {
		$this->_expression[$name] = $expression;
		$this->_variabletype[$name] = $type;
		// Add a new 'virtual' column to the list
		foreach ($this->_resultset as $i => $nada) {
			$this->_resultset[$i][$name] = '';
		}
	}

	/**
	 * Report::parseValue()
	 *
	 * @param $variable
	 * @param $value
	 * @return
	 **/
	function parseValue($variable, $value) {
		$type = $this->getVariableType($variable);
		switch ($type) {
			case "date" :
				$regex = $this->getDateRegex();
				if (eregi($regex, $value, $regs)) {
					if ($this->europeanstyle) {
						$newvalue = sprintf("%s/%s/%s", $regs[3], $regs[2], $regs[1]);
					} else {
						$newvalue = sprintf("%s/%s/%s", $regs[2], $regs[3], $regs[1]);
					}
				} else {
					$newvalue = $value;
				}
				break;

			case "datetime" :
				$regex = $this->_datetimeregex;
				if (eregi($regex, $value, $regs)) {
					$newvalue = sprintf("%s/%s/%s %s", $regs[3], $regs[2], $regs[1], $regs[4]);
				} else {
					$newvalue = $value;
				}
				break;

			case "money" :
				$thousand = $this->getThousandSeparator();
				$decimals = 2;
				$decpoint = $this->getDecimalPoint();
				if ($this->_negativestyle && $value < 0) {
					$value = - $value;
					$value = number_format($value, $decimals, $thousand, $decpoint);
					$value = $this->getNegativeStyle($value);
					$value = $this->getCurrencySymbol() . " " . $value;
				} else {
					$value = number_format($value, $decimals, $thousand, $decpoint);
					$value = $this->getCurrencySymbol() . " " . $value;
				}
				$newvalue = $value;
				break;

			case "select" :
				$combo = $this->combos[$variable];
				$i = 0;
				while ($combo[$i]) {
					if (trim($combo[$i]["value"]) == trim($value)) {
						if (ereg("^\s*$", $value)) {
							$newvalue = "";
						} else {
							$newvalue = $combo[$i]["label"];
						}
					}
					$i++;
				}
				break;

			case "numeric" :
				$thousand = $this->getThousandSeparator();
				$decimals = $this->getDecimals();
				$decpoint = $this->getDecimalPoint();
				if ($this->_negativestyle && $value < 0) {
					$value = - $value;
					$value = number_format($value, $decimals, $thousand, $decpoint);
					$value = $this->getNegativeStyle($value);
				} else {
					$value = number_format($value, $decimals, $thousand, $decpoint);
				}
				$newvalue = $value;
				break;

			case "percent" :
				if ($this->_nullforzero[$variable] && !$value) {
					$newvalue = "";
				}
				$thousand = $this->getThousandSeparator();
				$decimals = $this->getDecimals();
				$decpoint = $this->getDecimalPoint();
				if ($this->_negativestyle && $value < 0) {
					$value = - $value;
					$value = number_format($value, $decimals, $thousand, $decpoint) . "%";
					$value = $this->getNegativeStyle($value);
				} else {
					$value = number_format($value, $decimals, $thousand, $decpoint) . "%";
				}
				$newvalue = $value;

			default :
				$newvalue = $value;
		} // switch
		return $this->ifForm($variable, $newvalue);
	}

	/**
	 * Se o campo pertence a um form rederiza o tag com o valor
	 * jï¿½ formatado.
	 */
	function ifForm($var, $val) {
		if (isset($this->formfield[$var]) and $this->formfield[$var]) {
			$this->form->elements[$this->_namespace . $var]['ob']->value = $GLOBALS[$this->_namespace . $var];
			return $this->form->ge($this->_namespace . $var, $val);
		}
		return $val;
	}
	/**
	 *
	 * @access public
	 * @return string
	 **/
	function getDateRegex() {
		return $this->_dateregex;
	}

	/**
	 *
	 * @access public
	 * @return void
	 **/
	function setDateRegex($newValue) {
		$this->_dateregex = $newValue;
	}

	/**
	 *
	 * @access public
	 * @return integer
	 **/
	function getDecimals() {
		return $this->_decimals;
	}

	/**
	 *
	 * @access public
	 * @return void
	 **/
	function setDecimals($newValue) {
		$this->_decimals = $newValue;
	}

	/**
	 *
	 * @access public
	 * @return string
	 **/
	function getVariableType($name) {
		if (isset($this->_variabletype[$name]))
			return $this->_variabletype[$name];
		
		return null;
	}

	/**
	 *
	 * @access public
	 * @return array
	 **/
	function setVariableType($newValue) {
		$this->_variabletype = $newValue;
	}

	/**
	 *
	 * @access public
	 * @return string
	 **/
	function getNegativeStyle($value) {
		$string = $this->_negativestyle;
		$string = sprintf($string, $value);
		return $string;
	}

	/**
	 *
	 * @access public
	 * @return void
	 **/
	function setNegativeStyle($newValue) {
		$this->_negativestyle = $newValue;
	}

	/**
	 *
	 * @access public
	 * @return string
	 **/
	function getCurrencySymbol() {
		return $this->_cur_symbol;
	}

	/**
	 *
	 * @access public
	 * @return void
	 **/
	function setCurrencySymbol($newValue) {
		$this->_cur_symbol = $newValue;
	}

	/**
	 *
	 * @access public
	 * @return string
	 **/
	function getThousandSeparator() {
		return $this->_thousand_sep;
	}

	/**
	 *
	 * @access public
	 * @return void
	 **/
	function setThousandSeparator($newValue) {
		$this->_thousand_sep = $newValue;
	}
	/**
	 *
	 * @access public
	 * @return string
	 **/
	function getDecimalPoint() {
		return $this->_dec_point;
	}

	/**
	 *
	 * @access public
	 * @return void
	 **/
	function setDecimalPoint($newValue) {
		$this->_dec_point = $newValue;
	}

}
?>
